#ifndef CS488_VIEWER_HPP
#define CS488_VIEWER_HPP

#include <gtkmm.h>
#include <gtkglmm.h>
#include "scene.hpp"
#include "algebra.hpp"
#include "trackball.hpp"
#include <stack> 
#include <map> 
using Gtk::Menu_Helpers::MenuElem;

// The "main" OpenGL widget
class Viewer : public Gtk::GL::DrawingArea {
public:
  Viewer(SceneNode *n);
  virtual ~Viewer();
    
  // A useful function that forces this widget to rerender. If you
  // want to render a new frame, do not call on_expose_event
  // directly. Instead call this, which will cause an on_expose_event
  // call when the time is right.
  void invalidate();
    
    // mode type
    enum Mode {
        PositionOrientation,
        Joints
    };
    // app_menu functions:
    void reset_position();
    void reset_orientation();
    void reset_joints();
    void reset_all();
	// option_menu function
	void set_circle();
	void set_zbuffer();
	void set_backcull();
	void set_frontcull();
	// edit_menu functions
	void undo();
	void redo();
	void set_undo_redo_menu(Gtk::Menu* m);
	void set_option_menu(Gtk::Menu* m);
	void set_mode_menu(Gtk::Menu* m);
    // mode_menu functions:
    void set_mode(Mode m);
protected:

  // Events we implement
  // Note that we could use gtkmm's "signals and slots" mechanism
  // instead, but for many classes there's a convenient member
  // function one just needs to define that'll be called with the
  // event.

  // Called when GL is first initialized
  virtual void on_realize();
  // Called when our window needs to be redrawn
  virtual bool on_expose_event(GdkEventExpose* event);
  // Called when the window is resized
  virtual bool on_configure_event(GdkEventConfigure* event);
  // Called when a mouse button is pressed
  virtual bool on_button_press_event(GdkEventButton* event);
  // Called when a mouse button is released
  virtual bool on_button_release_event(GdkEventButton* event);
  // Called when the mouse moves
  virtual bool on_motion_notify_event(GdkEventMotion* event);

  // Draw a circle for the trackball, with OpenGL commands.
  // Assumes the context for the viewer is active.
  void draw_trackball_circle();
    // draw puppet
	void draw_puppet(bool picking = false);
    // picking
    void pick_joints(int x, int y);
    // post_pick
    void post_pick(GLint hits, GLuint *buffer);
	// update current stack
	void update_currentstack();
	// update edit menu
	void update_editmenu();
	// translate & rotate
	Matrix4x4 translation(const Vector3D& displacement);
	Matrix4x4 rotation(float fOldX, float fNewX, float fOldY, float fNewY);
	void initialize();
private:
    Mode m_mode;
    GLuint picking_buffer[50];
    SceneNode* puppet;
    bool button1, button2, button3;
    int start_x, start_y;
	bool m_circle, m_zbuffer, m_backcull, m_frontcull;
	Matrix4x4 m_translation, m_rotation;
	SceneNode* selected_joint;
	
	// stack
	struct JointNodeInfo{
		JointNode* address;
		double angle_x;
		double angle_y;
		JointNodeInfo(JointNode* addr, double x, double y):address(addr),angle_x(x),angle_y(y){}
	};
	typedef std::map<int, JointNodeInfo> STACKINFO;
	typedef std::stack<STACKINFO> STACK;
	STACK undo_stack;
	STACK redo_stack;
	STACKINFO current_stack;
	
	Gtk::Menu* edit_menu;
	Gtk::Menu* option_menu;
	Gtk::Menu* mode_menu;
};

#endif
